import time
import datetime
import traceback
from fastapi import Request, Response
from fastapi.responses import JSONResponse


IGNORE_METHOD = ('GET', 'HEAD')


async def validation_exception_handler(request, exc, *args, **kwargs):
    """参数校验异常"""
    record_http_error(request, detail=f'字段校验异常: {str(exc)}')
    return JSONResponse(status_code=422, content={'code': '-1', 'message': '参数校验异常,请检查参数'})


async def http_exception_handler(request, exc, *args, **kwargs):
    """http异常记录与捕获"""
    record_http_error(request, detail=exc.detail)
    return JSONResponse(status_code=exc.status_code, content={'detail': exc.detail})


def record_http_audit(request):
    router = request.scope.get('route')
    if not router:
        return

    summary = getattr(router, 'summary', '')  # 没有summary的不记录 （无用访问日志太多 设置了summary的才记录）
    if not summary:
        return

    openapi_extra = router.openapi_extra or {}
    auditor.info(app_context=request.query_params.get('app_context', 'default'),
                 user=getattr(request.state, 'user', {}).get('account', ''),
                 user_ip=request.state.ip,
                 event=openapi_extra.get('sub_module', ''),
                 sub_event=summary,
                 result='成功',
                 description=router.description)


def record_http_error(request, detail=''):
    router = request.scope.get('route')
    if not router:
        return

    summary = getattr(router, 'summary', '')  # 没有summary的不记录 （无用访问日志太多 设置了summary的才记录）
    if not summary:
        return

    openapi_extra = getattr(router, 'openapi_extra', {})
    auditor.error(app_context=request.query_params.get('app_context', 'default'),
                  account=getattr(request.state, 'user', {}).get('account', ''),
                  ip=request.state.ip,
                  module=openapi_extra.get('module', ''),
                  sub_module=openapi_extra.get('sub_module', ''),
                  func=router.name,
                  event='失败',
                  description=router.description,
                  detail=detail)


async def custom_http_log(request: Request, call_next):
    start_time = time.time()
    request.state.ip = (request.headers.get('X-Real-IP') or request.headers.get('X-Forwarded-For') or
                        request.client.host)  # 真实IP

    response: Response = await call_next(request)
    process_time = time.time() - start_time
    message = {
        'datetime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        'scheme': request.base_url.scheme,
        'hostname': request.base_url.hostname,
        'dest': request.scope.get('server')[0] if request.scope.get('server') else request.base_url.hostname,
        'dest_port': request.base_url.port,
        'src': getattr(request.state, 'ip'),
        'src_port': request.client.port,
        'path': request.url.path,
        'query': request.url.query,
        'fragment': request.url.fragment,
        'method': request.method,
        'status_code': response.status_code,
        'cost': format(process_time, '.4f'),
        'level': 'info' if response.status_code in range(200, 400) else 'error',
        'account': getattr(request.state, 'user', {}).get('account', '')
    }
    http_logger.logger.info(message)

    if request.scope.get('method') in IGNORE_METHOD:  # 这些方法不记
        return response

    if message.get('level') == 'info':
        record_http_audit(request)
    return response


async def custom_http_response(request: Request, call_next):
    """内部错误捕获 统一返回异常"""
    try:
        response = await call_next(request)
        return response
    except Exception as e:
        print(traceback.format_exc())
        record_http_error(request, detail=str(e))
        return JSONResponse(status_code=500, content={'code': 5, 'message': '服务暂不可用'})
